package osmedile.intellij.checkmyvars;

import com.intellij.ide.util.PackageUtil;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.module.ModuleType;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.roots.ProjectRootManager;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElementFactory;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiManager;
import com.intellij.util.IncorrectOperationException;
import osmedile.intellij.util.action.WriteAction;

/**
 * @author Olivier Smedile
 * @version $Id$
 */
public class CreateSampleAction extends WriteAction {

    public static final String ACTION_ID = "osmedile.intellij.checkmyvars.CreateSampleAction";


    public void doAction(Project project, DataContext dataContext) {
        if (project == null) {
            return;
            
        }

        Module javaModule = null;

        final ProjectRootManager projManager = ProjectRootManager.getInstance(project);
        if (projManager == null) {
            return;
        }

        VirtualFile[] srcs = projManager.getContentSourceRoots();
        Module[] modules = ModuleManager.getInstance(project).getModules();
        for (int i = 0; i < modules.length && javaModule == null; i++) {
            Module module = modules[i];
            if (module.getModuleType().equals(ModuleType.JAVA)) {
                javaModule = module;
            }
        }
        if (javaModule == null && modules.length > 0) {
            javaModule = modules[0];
        }

        if (javaModule != null) {
            boolean sampleCreated = false;
            for (int i = 0; i < srcs.length && !sampleCreated; i++) {
                VirtualFile src = srcs[i];
                if (src.isValid() && src.isWritable()) {

                    sampleCreated = createSample(project, javaModule, src);
                }
            }
        }
    }


    protected boolean createSample(Project project, Module javaModule, VirtualFile src) {
        boolean sampleCreated;
        final PsiManager psiManager = PsiManager.getInstance(project);
        final PsiElementFactory psiElFactory = psiManager.getElementFactory();
        PsiDirectory srcDir = psiManager.findDirectory(src);

        try {
            PsiDirectory checkMyVars =
                    PackageUtil.findOrCreateDirectoryForPackage(javaModule, "checkmyvars", srcDir, false);
            if (checkMyVars == null) {
                //TODO true?
                return true;
            }

            if (checkMyVars.getVirtualFile().findChild("Default.java") == null) {
                PsiFile anno = psiElFactory.createFileFromText("Default.java", "package checkmyvars;\n" +
                        "\n" +
                        "@java.lang.annotation.Documented\n" +
                        "@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.CLASS)\n" +
                        "@java.lang.annotation.Target(\n" +
                        "        value = {java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD,\n" +
                        "                java.lang.annotation.ElementType.PARAMETER,\n" +
                        "                java.lang.annotation.ElementType.LOCAL_VARIABLE})\n" +
                        "public @interface Default {\n" +
                        "\n" +
                        "    String value() default \"\";\n" +
                        "}");
                checkMyVars.add(anno);
            }

            if (checkMyVars.getVirtualFile().findChild("Email.java") == null) {
                PsiFile anno = psiElFactory.createFileFromText("Email.java", "package checkmyvars;\n" +
                        "\n" +
                        "@java.lang.annotation.Documented\n" +
                        "@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.CLASS)\n" +
                        "@java.lang.annotation.Target(\n" +
                        "        value = {java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD,\n" +
                        "                java.lang.annotation.ElementType.PARAMETER,\n" +
                        "                java.lang.annotation.ElementType.LOCAL_VARIABLE})\n" +
                        "public @interface Email {\n" +
                        "\n" +
                        "}");
                checkMyVars.add(anno);
            }


            if (checkMyVars.getVirtualFile().findChild("Match.java") == null) {
                PsiFile anno = psiElFactory.createFileFromText("Match.java",
                        "package checkmyvars;\n" +
                                "\n" +
                                "@java.lang.annotation.Documented\n" +
                                "@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.CLASS)\n" +
                                "@java.lang.annotation.Target(\n" +
                                "        value = {java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD,\n" +
                                "                java.lang.annotation.ElementType.PARAMETER,\n" +
                                "                java.lang.annotation.ElementType.LOCAL_VARIABLE})\n" +
                                "public @interface Match {\n" +
                                "\n" +
                                "    public abstract String value() default \"\";\n" +
                                "}");
                checkMyVars.add(anno);
            }

            if (checkMyVars.getVirtualFile().findChild("MinMax.java") == null) {
                PsiFile anno = psiElFactory.createFileFromText("MinMax.java", "package checkmyvars;\n" +
                        "\n" +
                        "@java.lang.annotation.Documented\n" +
                        "@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.CLASS)\n" +
                        "@java.lang.annotation.Target(\n" +
                        "        value = {java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD,\n" +
                        "                java.lang.annotation.ElementType.PARAMETER,\n" +
                        "                java.lang.annotation.ElementType.LOCAL_VARIABLE})\n" +
                        "public @interface MinMax {\n" +
                        "\n" +
                        "    int min() default 0;\n" +
                        "\n" +
                        "    int max() default Integer.MAX_VALUE;\n" +
                        "}");
                checkMyVars.add(anno);
            }
            if (checkMyVars.getVirtualFile().findChild("Max.java") == null) {
                PsiFile anno = psiElFactory.createFileFromText("Max.java", "package checkmyvars;\n" +
                        "\n" +
                        "@java.lang.annotation.Documented\n" +
                        "@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.CLASS)\n" +
                        "@java.lang.annotation.Target(\n" +
                        "        value = {java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD,\n" +
                        "                java.lang.annotation.ElementType.PARAMETER,\n" +
                        "                java.lang.annotation.ElementType.LOCAL_VARIABLE})\n" +
                        "public @interface Max {\n" +
                        "\n" +
                        "    double value() default Double.MAX_VALUE;\n" +
                        "}");
                checkMyVars.add(anno);
            }
            if (checkMyVars.getVirtualFile().findChild("Min.java") == null) {
                PsiFile anno = psiElFactory.createFileFromText("Min.java", "package checkmyvars;\n" +
                        "\n" +
                        "@java.lang.annotation.Documented\n" +
                        "@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.CLASS)\n" +
                        "@java.lang.annotation.Target(\n" +
                        "        value = {java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD,\n" +
                        "                java.lang.annotation.ElementType.PARAMETER,\n" +
                        "                java.lang.annotation.ElementType.LOCAL_VARIABLE})\n" +
                        "public @interface Min {\n" +
                        "\n" +
                        "    double value() default 0;\n" +
                        "}");
                checkMyVars.add(anno);
            }


            if (checkMyVars.getVirtualFile().findChild("NotBlank.java") == null) {
                PsiFile anno = psiElFactory.createFileFromText("NotBlank.java",
                        "package checkmyvars;\n" +
                                "\n" +
                                "@java.lang.annotation.Documented\n" +
                                "@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.CLASS)\n" +
                                "@java.lang.annotation.Target(\n" +
                                "        value = {java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD,\n" +
                                "                java.lang.annotation.ElementType.PARAMETER,\n" +
                                "                java.lang.annotation.ElementType.LOCAL_VARIABLE})\n" +
                                "public @interface NotBlank {\n" +
                                "}");
                checkMyVars.add(anno);
            }

            if (checkMyVars.getVirtualFile().findChild("NotEmpty.java") == null) {
                PsiFile anno = psiElFactory.createFileFromText("NotEmpty.java",
                        "package checkmyvars;\n" +
                                "\n" +
                                "@java.lang.annotation.Documented\n" +
                                "@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.CLASS)\n" +
                                "@java.lang.annotation.Target(\n" +
                                "        value = {java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD,\n" +
                                "                java.lang.annotation.ElementType.PARAMETER,\n" +
                                "                java.lang.annotation.ElementType.LOCAL_VARIABLE})\n" +
                                "public @interface NotEmpty {\n" +
                                "}");
                checkMyVars.add(anno);
            }

            if (checkMyVars.getVirtualFile().findChild("NotNull.java") == null) {
                PsiFile anno = psiElFactory.createFileFromText("NotNull.java",
                        "package checkmyvars;\n" +
                                "\n" +
                                "import java.lang.annotation.Documented;\n" +
                                "import java.lang.annotation.ElementType;\n" +
                                "import java.lang.annotation.Retention;\n" +
                                "import java.lang.annotation.RetentionPolicy;\n" +
                                "import java.lang.annotation.Target;\n" +
                                "\n" +
                                "@Documented\n" +
                                "@Retention(RetentionPolicy.CLASS)\n" +
                                "@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})\n" +
                                "public @interface NotNull {\n" +
                                "}");
                checkMyVars.add(anno);
            }

            if (checkMyVars.getVirtualFile().findChild("Nullable.java") == null) {
                PsiFile anno = psiElFactory.createFileFromText("Nullable.java",
                        "package checkmyvars;\n" +
                                "\n" +
                                "import java.lang.annotation.Documented;\n" +
                                "import java.lang.annotation.ElementType;\n" +
                                "import java.lang.annotation.Retention;\n" +
                                "import java.lang.annotation.RetentionPolicy;\n" +
                                "import java.lang.annotation.Target;\n" +
                                "\n" +
                                "@Documented\n" +
                                "@Retention(RetentionPolicy.CLASS)\n" +
                                "@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})\n" +
                                "public @interface Nullable {\n" +
                                "}");
                checkMyVars.add(anno);
            }

            if (checkMyVars.getVirtualFile().findChild("Size.java") == null) {
                PsiFile anno = psiElFactory.createFileFromText("Size.java", "package checkmyvars;\n" +
                        "\n" +
                        "@java.lang.annotation.Documented\n" +
                        "@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.CLASS)\n" +
                        "@java.lang.annotation.Target(\n" +
                        "        value = {java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD,\n" +
                        "                java.lang.annotation.ElementType.PARAMETER,\n" +
                        "                java.lang.annotation.ElementType.LOCAL_VARIABLE})\n" +
                        "public @interface Size {\n" +
                        "\n" +
                        "    int min() default 0;\n" +
                        "\n" +
                        "    int max() default Integer.MAX_VALUE;\n" +
                        "}");
                checkMyVars.add(anno);
            }

            if (checkMyVars.getVirtualFile().findChild("ToBoolean.java") == null) {
                PsiFile anno = psiElFactory.createFileFromText("ToBoolean.java", "package checkmyvars;\n" +
                        "\n" +
                        "@java.lang.annotation.Documented\n" +
                        "@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.CLASS)\n" +
                        "@java.lang.annotation.Target(\n" +
                        "        value = {java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD,\n" +
                        "                java.lang.annotation.ElementType.PARAMETER,\n" +
                        "                java.lang.annotation.ElementType.LOCAL_VARIABLE})\n" +
                        "public @interface ToBoolean {\n" +
                        "\n" +
                        "    abstract String[] yes() default {\"true\", \"1\"};\n" +
                        "    abstract String[] no() default {\"false\", \"0\"};\n" +
                        "\n" +
                        "}");
                checkMyVars.add(anno);
            }
            if (checkMyVars.getVirtualFile().findChild("ToDate.java") == null) {
                PsiFile anno = psiElFactory.createFileFromText("ToDate.java", "package checkmyvars;\n" +
                        "\n" +
                        "@java.lang.annotation.Documented\n" +
                        "@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.CLASS)\n" +
                        "@java.lang.annotation.Target(\n" +
                        "        value = {java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD,\n" +
                        "                java.lang.annotation.ElementType.PARAMETER,\n" +
                        "                java.lang.annotation.ElementType.LOCAL_VARIABLE})\n" +
                        "public @interface ToDate {\n" +
                        "\n" +
                        "    String value() default \"yyyy/MM/dd\";\n" +
                        "}");
                checkMyVars.add(anno);
            }
            if (checkMyVars.getVirtualFile().findChild("ToNumeric.java") == null) {
                PsiFile anno = psiElFactory.createFileFromText("ToNumeric.java", "package checkmyvars;\n" +
                        "\n" +
                        "@java.lang.annotation.Documented\n" +
                        "@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.CLASS)\n" +
                        "@java.lang.annotation.Target(\n" +
                        "        value = {java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD,\n" +
                        "                java.lang.annotation.ElementType.PARAMETER,\n" +
                        "                java.lang.annotation.ElementType.LOCAL_VARIABLE})\n" +
                        "public @interface ToNumeric {\n" +
                        "}");
                checkMyVars.add(anno);
            }
            if (checkMyVars.getVirtualFile().findChild("ToURL.java") == null) {
                PsiFile anno = psiElFactory.createFileFromText("ToURL.java", "package checkmyvars;\n" +
                        "\n" +
                        "@java.lang.annotation.Documented\n" +
                        "@java.lang.annotation.Retention(value = java.lang.annotation.RetentionPolicy.CLASS)\n" +
                        "@java.lang.annotation.Target(\n" +
                        "        value = {java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.FIELD,\n" +
                        "                java.lang.annotation.ElementType.PARAMETER,\n" +
                        "                java.lang.annotation.ElementType.LOCAL_VARIABLE})\n" +
                        "public @interface ToURL {\n" +
                        "}");
                checkMyVars.add(anno);
            }

            //Create sample java file
            PsiDirectory samplePkg =
                    PackageUtil
                            .findOrCreateDirectoryForPackage(javaModule, "checkmyvars.sample", srcDir, false);


            PsiFile completeSample = samplePkg.findFile("CompleteSample.java");

            if (completeSample == null) {
                completeSample = psiElFactory.createFileFromText("CompleteSample.java",
                        "package checkmyvars.sample;\n" +
                                "\n" +
                                "\n" +
                                "import checkmyvars.*;\n" +
                                "\n" +
                                "import java.util.*;\n" +
                                "\n" +
                                "\n" +
                                "/**\n" +
                                " *\n" +
                                " */\n" +
                                "public class CompleteSample {\n" +
                                "\n" +
                                "    public static final String DEFAULT_VALUE = \"defaultValue\";\n" +
                                "\n" +
                                "\n" +
                                "    public CompleteSample(@NotNull String str) {\n" +
                                "        super();\n" +
                                "    }\n" +
                                "\n" +
                                "    public void notEmptyString(@NotEmpty String str) {\n" +
                                "\n" +
                                "\n" +
                                "    }\n" +
                                "\n" +
                                "    public void notEmptyCollection(@NotEmpty List list) {\n" +
                                "\n" +
                                "    }\n" +
                                "\n" +
                                "    public void notEmptyMap(@NotEmpty Map<String, String> s) {\n" +
                                "\n" +
                                "\n" +
                                "    }\n" +
                                "\n" +
                                "    public void notEmptyArray(@NotEmpty String[] array, @NotEmpty String... varargs) {\n" +
                                "\n" +
                                "\n" +
                                "    }\n" +
                                "\n" +
                                "    public void notBlankString(@NotBlank String str) {\n" +
                                "\n" +
                                "\n" +
                                "    }\n" +
                                "\n" +
                                "\n" +
                                "    public void defaultForString(@Default(DEFAULT_VALUE)String var) {\n" +
                                "\n" +
                                "\n" +
                                "    }\n" +
                                "\n" +
                                "    public void defaultForVarargs(@Default String[] array, @Default String... varargs) {\n" +
                                "\n" +
                                "\n" +
                                "    }\n" +
                                "\n" +
                                "    public void defaultForCollection(@Default List<String> var) {\n" +
                                "\n" +
                                "    }\n" +
                                "\n" +
                                "    public void defaultForMap(@Default Map<String, String> var) {\n" +
                                "\n" +
                                "    }\n" +
                                "\n" +
                                "\n" +
                                "    public void sizeForString(@Size String var) {\n" +
                                "\n" +
                                "\n" +
                                "    }\n" +
                                "\n" +
                                "    public void sizeForVarargs(@Size String[] array, @Size String... varargs) {\n" +
                                "\n" +
                                "\n" +
                                "    }\n" +
                                "\n" +
                                "    public void sizeForCollection(@Size List<String> list, @Size Map<String, String> amp) {\n" +
                                "\n" +
                                "    }\n" +
                                "\n" +
                                "    public void matchForString(@Match String str) {\n" +
                                "\n" +
                                "    }\n" +
                                "\n" +
                                "// number\n" +
                                "\n" +
                                "    public void minMaxForNumber(@MinMax int nbInt, @MinMax(min = 6)short nbShort, @MinMax(max = 5)Double nbDouble) {\n" +
                                "\n" +
                                "    }\n" +
                                "\n" +
                                "    public void minForNumber(@Min int nbInt, @Min(6)short nbShort, @Min(5.5)Double nbDouble) {\n" +
                                "\n" +
                                "    }\n" +
                                "\n" +
                                "    public void maxForNumber(@Max int nbInt, @Max(6.65)short nbShort, @Max(5)Double nbDouble) {\n" +
                                "\n" +
                                "    }\n" +
                                "\n" +
                                "    public void minMaxForString(@MinMax String str) {\n" +
                                "\n" +
                                "    }\n" +
                                "\n" +
                                "//  String conversion\n" +
                                "\n" +
                                "\n" +
                                "    public void dateForString(@ToDate String str) {\n" +
                                "\n" +
                                "    }\n" +
                                "\n" +
                                "    public void boolForString(@ToBoolean String str) {\n" +
                                "\n" +
                                "    }\n" +
                                "\n" +
                                "\n" +
                                "    public void numericForString(@ToNumeric String str) {\n" +
                                "\n" +
                                "    }\n" +
                                "\n" +
                                "    public void urlForString(@ToURL String str) {\n" +
                                "\n" +
                                "    }\n" +
                                "\n" +
                                "\n" +
                                "\n" +
                                "    public void emailForString(@Email String str) {\n" +
                                "\n" +
                                "    }\n" +
                                "\n" +
                                "\n" +
                                "}");
                samplePkg.add(completeSample);
            }


            PsiFile sample = samplePkg.findFile("Sample.java");

            if (sample == null) {
                sample = psiElFactory.createFileFromText("Sample.java",
                        "package checkmyvars.sample;\n" +
                                "\n" +
                                "import checkmyvars.NotNull;\n" +
                                "import checkmyvars.MinMax;\n" +
                                "\n" +
                                "/**\n" +
                                " * Simple sample to show basic code generation of checkmyvars\n" +
                                " */\n" +
                                "public class Sample {\n" +
                                "\n" +
                                "    /**\n" +
                                "     * This should generate a simple not null check\n" +
                                "     */\n" +
                                "    public void notNull(@NotNull String var) {\n" +
                                "        \n" +
                                "    }\n" +
                                "\n" +
                                "    /**\n" +
                                "     * Step 1. Generate code using \"Code > Generate > Check my vars\" menu\n" +
                                "     * Step 2. Modify \"min\" and \"max\" parameter\n" +
                                "     * Step 3. Regenerate the code and notice that parameter value are updated\n" +
                                "     */\n" +
                                "    public void minMaxNumber(@MinMax(min = 1, max = 5)int nb) {\n" +
                                "        \n" +
                                "    }\n" +
                                "}");
                samplePkg.add(sample);


            }

            sample = samplePkg.findFile("Sample.java");
            sample.navigate(true);

            sampleCreated = true;
        } catch (IncorrectOperationException e) {
            throw new RuntimeException(e);
        }
        return sampleCreated;
    }


}